package cn.sstir.api.interceptor;

import cn.sstir.api.common.util.ApiSignatureUtil;
import cn.sstir.api.common.util.RedisUtil;
import cn.sstir.api.common.util.StringUtil;
import cn.sstir.api.common.util.WebRenderUtil;
import cn.sstir.api.entity.ClientDetails;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import static cn.sstir.api.common.constants.ClientConstants.*;
import static cn.sstir.api.common.exception.ApiSignatureExceptionEnum.*;
import static cn.sstir.api.common.exception.ResponseEntityExceptionEnum.MISSING_SERVLET_REQUEST_PARAMETER_ERROR;
import static cn.sstir.fastspringboot.core.exception.enums.CoreExceptionEnum.UNAUTHORIZED;

/**
 * @title: ApiSignatureInterceptor
 * @description: Api签名认证
 * @author: fli
 * @email: fli@sstir.cn
 * @date: 2019/8/13 15:41
 */
@Slf4j
public class ApiSignatureInterceptor extends HandlerInterceptorAdapter {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 获取url中的时间戳
        String timestamp = request.getParameter(TIMESTAMP_KEY);
        // 获取url中的client_id
        String clientId = request.getParameter(CLIENT_ID);
        // 获取url中的签名
        String sign = request.getParameter(SIGN_KEY);

        if (StringUtil.isBlank(timestamp) || StringUtil.isBlank(clientId) || StringUtil.isBlank(sign)) {
            WebRenderUtil.renderErrorJson(response,MISSING_SERVLET_REQUEST_PARAMETER_ERROR );
            return false;
        }


        /**
         * 检查时间戳格式是否正确
         */
        if (!StringUtil.isNumeric(timestamp)) {
            WebRenderUtil.renderErrorJson(response, TIMESTAMP_ERROR);
            return false;
        }

        /**
         * 检查clientId是否合法
         */
        if (StringUtil.isEmpty(clientId) || (!RedisUtil.hasKey(REDIS_CLIENT_PACKAGE_NAME + clientId))) {
            WebRenderUtil.renderErrorJson(response, CLIENTID_ERROR);
            return false;
        }

        /**
         * 检查签名是否超时
         */
        Long ts = Long.valueOf(timestamp);
        if (System.currentTimeMillis() / 1000 - ts > SIGN_EXPIRED_TIME) {
            WebRenderUtil.renderErrorJson(response, EXPIRE_ERROR);
            return false;
        }


        ClientDetails clientDetails = RedisUtil.get(REDIS_CLIENT_PACKAGE_NAME + clientId, ClientDetails.class);

        /**
         * 检查签名是否正确
         */
        if (!verifySignature(request, clientDetails.getClientSecret())) {
            WebRenderUtil.renderErrorJson(response, SIGN_ERROR);
            return false;
        }

        /**
         * 检查是否具有访问权限
         */
        if (!verifyPermission(request, clientDetails)) {
            WebRenderUtil.renderErrorJson(response, UNAUTHORIZED);
            return false;
        }

        return true;
    }

    private boolean verifyPermission(HttpServletRequest request, ClientDetails clientDetails) {
        String URL = request.getRequestURI();
        log.info("request url is : {}", URL);
        String allowedResourcePath = clientDetails.getAllowedResourcePath();
        String[] paths = StringUtil.split(allowedResourcePath, ",");
        boolean matches = false;
        for (String path : paths) {
            if (StringUtil.match(path, URL)) {
                matches = true;
                break;
            }
        }
        return matches;
    }


    private boolean verifySignature(HttpServletRequest request, String clientSecret) {
        Enumeration<?> pNames = request.getParameterNames();
        Map<String, Object> params = new HashMap<>();
        while (pNames.hasMoreElements()) {
            String pName = (String) pNames.nextElement();
            if (SIGN_KEY.equals(pName)) continue;
            Object pValue = request.getParameter(pName);
            params.put(pName, pValue);
        }

        String originSign = request.getParameter(SIGN_KEY);
        String sign = ApiSignatureUtil.generateSign(params, clientSecret);
        return sign.equals(originSign);
    }
}
